package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

func main() {
	r := gin.Default()
	r.Use(Cors())
	r.StaticFile("/", "./web/index.html")
	r.Static("/assets", "./web/assets")
	r.Static("/uploads", "./uploads")
	r.POST("/upload", UploadHandler)
	s := &http.Server{
		Addr:           ":8889", // 服务器地址
		Handler:        r,       // 使用的处理器，这里为 gin.Engine
		ReadTimeout:    60 * time.Second,
		WriteTimeout:   60 * time.Second,
		MaxHeaderBytes: 1 << 20, // 请求头的最大字节数
	}

	s.ListenAndServe()
}

func UploadHandler(c *gin.Context) {
	// 获取传递的版本号
	color := c.PostForm("color")
	if color == "" {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "缺少颜色参数"})
		return
	}
	// 获取上传的文件
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "参数错误"})
		return
	}

	// 检查文件大小是否超过20MB
	const maxUploadSize = 20 << 20 // 20MB
	if header.Size > maxUploadSize {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "文件大小超过限制"})
		return
	}

	// 检查文件类型是否为照片
	buffer := make([]byte, 512) // 只需要读取文件头部即可检测类型
	if _, err := file.Read(buffer); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "文件读取错误"})
		return
	}
	fileType := http.DetectContentType(buffer)
	if fileType != "image/jpeg" && fileType != "image/png" && fileType != "image/gif" {
		c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "文件类型只能为照片"})
		return
	}

	// 重置文件读取指针
	file.Seek(0, 0)

	// 获取当前工作目录
	dir, err := os.Getwd()
	if err != nil {
		fmt.Println("获取当前工作目录失败")
	}
	// 构建相对路径
	uuidFileName := filepath.Join(dir, "/uploads/"+uuid.New().String()+filepath.Ext(header.Filename))

	// 创建本地文件
	f, err := os.OpenFile(uuidFileName, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "文件创建失败"})
		return
	}
	defer f.Close()

	// 保存文件
	if _, err := io.Copy(f, file); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "文件写入失败"})
		return
	}

	// 文件保存成功后，调用 HumanSeg.exe
	exePath := filepath.Join(dir, "HumanSeg.exe")
	inputFilePath := uuidFileName // 上传的文件路径
	// 生成输出文件的路径
	baseName := filepath.Base(uuidFileName)                                     // 获取原文件名（包括扩展名）
	ext := filepath.Ext(uuidFileName)                                           // 获取文件扩展名
	outputBaseName := baseName[:len(baseName)-len(ext)] + "_output" + ext       // 构建输出文件名
	outputFilePath := filepath.Join(filepath.Dir(uuidFileName), outputBaseName) // 构建输出文件的完整路径

	shortInputPath := "/uploads/" + filepath.Base(inputFilePath)
	shortOutputPath := "/uploads/" + filepath.Base(outputFilePath)

	// 构建命令
	cmd := exec.Command(exePath, "-i="+inputFilePath, "-o="+outputFilePath, "-bk="+color)

	//获取当前时间
	ticksNow := time.Now()
	fmt.Println(ticksNow.Format("2006-01-02 15:04:05:") + "人像抠图开始")
	fmt.Printf("执行命令：%s\n", strings.Join(cmd.Args, " "))

	err = cmd.Run()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "运行外部程序失败"})
		return
	}

	ticksNow = time.Now()
	//格式化打印当前时间
	fmt.Println(ticksNow.Format("2006-01-02 15:04:05:") + "人像抠图结束")

	// 检查输出文件是否存在
	if _, err := os.Stat(outputFilePath); os.IsNotExist(err) {
		c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "输出文件不存在，外部程序可能运行失败"})
		return
	}

	// 外部程序运行成功且输出文件存在，返回响应
	c.JSON(http.StatusOK, gin.H{
		"code":       200,
		"msg":        "文件上传并处理成功",
		"inputFile":  shortInputPath,
		"outputFile": shortOutputPath,
	})
}

// Cors 直接放行所有跨域请求并放行所有 OPTIONS 方法
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin")
		c.Header("Access-Control-Allow-Origin", origin)
		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id")
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At")
		c.Header("Access-Control-Allow-Credentials", "true")
		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		// 处理请求
		c.Next()
	}
}
